﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Slb.Ocean.Core;
using Slb.Ocean.Petrel.UI;
using Slb.Ocean.Petrel.Basics;
using Slb.Ocean.Petrel.DomainObject;
using Public.Ocean.Petrel.Extensions.Internal;

namespace Public.Ocean.Petrel.Extensions.Services
{
    /// <summary>
    /// Returns references to service implementations for a given object. The services are located in the ServiceLocator.
    /// </summary>
    public class PetrelServiceWrapper
    {
        IPerTypeServiceProvider locator;
        object obj;

        public PetrelServiceWrapper(IPerTypeServiceProvider locator, object obj)
        {
            this.locator = locator;
            this.obj = obj;
        }

        public object Object { get { return obj; } }

        private TResult Produce<TFactory, TResult>(Func<TFactory, TResult> factory, Func<TResult> fallback) where TFactory : class
        {
            var service = locator.GetService(obj, typeof(TFactory)) as TFactory;
            if (service != null)
            {
                var result = factory(service);
                if (!object.ReferenceEquals(result, default(TResult)))
                {
                    return result;
                }
            }
            return fallback();
        }

        public IPresentation Presentation { get { return Produce<IPresentationFactory, IPresentation>(f => f.GetPresentation(obj), () => new Presentation((obj ?? "NULL").ToString())); } }
        public ISettingsInfo Settings { get { return Produce<ISettingsInfoFactory, ISettingsInfo>(f => f.GetSettingsInfo(obj), () => new DefaultSettingsInfo()); } }
        public IElementBehavior Behavior { get { return Produce<IElementBehaviorFactory, IElementBehavior>(f => f.GetElementBehavior(obj), () => new DefaultElementBehavior()); } }
        public IObservableElementEnumerable Children { get { return Produce<IObservableElementEnumerableFactory, IObservableElementEnumerable>(f => f.GetEnumerable(obj), () => new DefaultObservableElementEnumerable()); } }
        public IHistoryInfo History { get { return Produce<IHistoryInfoFactory, IHistoryInfo>(f => f.GetHistoryInfo(obj), () => new DefaultHistoryInfo()); } }
        public IPropertySettingsInfo PropertySettings { get { return Produce<IPropertySettingsInfoFactory, IPropertySettingsInfo>(f => f.GetPropertySettingsInfo(obj), () => new DefaultPropertySettingsInfo()); } }
        public IDomainSettingsInfo DomainSettings { get { return Produce<IDomainSettingsInfoFactory, IDomainSettingsInfo>(f => f.GetDomainSettingsInfo(obj), () => new DefaultDomainSettingsInfo()); } }
        public Statistics Statistics { get { return Produce<IStatisticsFactory, IStatisticsFactory>(f => f, () => new DefaultStatistics()).GetStatistics(obj); } }
        public IParentSource Parent { get { return Produce<IParentSourceFactory, IParentSource>(f => f.GetParentSource(obj), () => new DefaultParentSource()); } }

        /// <summary>
        /// Enumerates all the child objects recursively.
        /// </summary>
        /// <returns></returns>
        public System.Collections.IEnumerable EnumerateRecursive()
        {
            return Enumerations.Flatten(new[] { obj }, o => new PetrelServiceWrapper(locator, o).Children);
        }

        /// <summary>
        /// Enumerates all the child objects recursively. Returns the service wrapper. 
        /// This is to prevent throwing away the already created wrapper.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<PetrelServiceWrapper> EnumerateWrappersRecursive()
        {
            return Enumerations.Flatten(Wrap(new[] { obj }), w => Wrap(w.Children));
        }

        /// <summary>
        /// Wraps an enumerable with service wrappers.
        /// </summary>
        /// <param name="items">The items.</param>
        /// <returns></returns>
        private IEnumerable<PetrelServiceWrapper> Wrap(System.Collections.IEnumerable items)
        {
            foreach (var item in items)
            {
                yield return new PetrelServiceWrapper(locator, item);
            }
        }

        /// <summary>
        /// Enumerates all the child objects recursively.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public System.Collections.IEnumerable EnumerateRecursive(Func<object, bool> predicate)
        {
            if (predicate == null) throw new ArgumentNullException("predicate");

            foreach (var obj in EnumerateRecursive())
            {
                if (predicate(obj))
                {
                    yield return obj;
                }
            }
        }

        /// <summary>
        /// Enumerates all the child objects recursively.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="predicate">Match predicate.</param>
        /// <returns></returns>
        public IEnumerable<TResult> EnumerateRecursive<TResult>(Func<TResult, bool> predicate) where TResult : class
        {
            if (predicate == null) throw new ArgumentNullException("predicate");

            foreach (var obj in EnumerateRecursive())
            {
                TResult result = obj as TResult;
                if (result != null && predicate(result))
                {
                    yield return result;
                }
            }
        }

        /// <summary>
        /// Enumerates all the child objects recursively.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <returns></returns>
        public IEnumerable<TResult> EnumerateRecursive<TResult>() where TResult : class
        {
            return EnumerateRecursive<TResult>(obj => true);
        }
    }
}
